home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Forever 4
/
Atari Forever 4.zip
/
Atari Forever 4.iso
/
SERIE_AI
/
AI_027
/
CFAT_R3.LZH
/
archive.lzh
/
checkfat.c
< prev
next >
Wrap
Text File
|
1996-03-17
|
48KB
|
1,714 lines
/*3456789012345678901234567890123456789012345678*/
/*
* checkfat.c vom 13.03.1996
*
* Autor:
* Thomas Binder
* (binder@rbg.informatik.th-darmstadt.de)
*
* Zweck:
* FAT-Testprogramm für den Aufruf in einer Shell
* oder (mit kleinem Hilfsprogramm) aus dem Auto-
* Ordner. Findet falsche Dateilängen, illegale
* Start- und Folgecluster, Clusterschleifen,
* Ordnerschleifen, Files mit als defekt
* markierten Clustern, Cluster mit mehreren
* Vorgängern, falsche Clustereinträge, verwaiste
* Cluster und mehrfach belegte Cluster. Ebenso
* werden alle als defekt markierten Cluster
* gefunden. Mittels Kommandozeilenoptionen kann
* die Ausgabe aller Filenamen, zusätzlich die
* Ausgabe sämtlicher Cluster jedes Files sowie
* eine detailierte Ergebnis-Ausgabe eingeschaltet
* werden.
* Wichtig: Nur für 16-Bit-FATs geeignet!
*
* Vielen Dank auch an meine Betatester (in alpha-
* betischer Reihenfolge):
* Alexander Clauss, Dirk Klemmt, Rainer Riedl,
* Michael Schwingen, Uwe Seimet, Manfred Ssykor
* und Arno Welzel.
*
* History:
* Irgendwann 1993: Erstellung
* 23.03.1995: Deutliche Verbesserung. CheckFat
* findet jetzt auch Clusterschleifen,
* illegale Folgecluster und ist auf
* die Steuerung der Ausgabe über
* Kommandozeilenoptionen vorbereitet.
* 24.03.1995: CheckFat findet jetzt auch
* Ordnerschleifen und Cluster mit
* mehreren Vorgängern.
* 26.03.1995: Da neuere GEMDOS-Versionen wirklich
* alle Datencluster belegen können,
* werden jetzt Clusternummern von 2
* bis numcl + 1 akzeptiert (bisher
* nur bis numcl - 1). Außerdem wurden
* jetzt die Steuerung per
* Kommandozeile integriert und einige
* interne Optimierungen vorgenommen.
* 27.03.1995: Beginn der Kommentierung, dabei
* noch kleinere Optimierungen und
* Fehlerbeseitigungen.
* 29.03.1995: "Fehler" in der Meldung bei Start
* ohne Parameter ausgebaut (fehlendes
* Newline). Außerdem darf die
* Laufwerksangabe jetzt auch mehr als
* einen Buchstaben lang sein, der
* Rest wird einfach ignoriert (damit
* kann beispielsweise auch c: oder
* c:\ übergeben werden)
* 31.03.1995: Anpassungen an MiNTLib
* 24.07.1995: Neue Kommandozeilenoption -h für
* Tastendruck vor Programmende.
* 10.10.1995: CheckFat merkt sich jetzt auch alle
* Dateinamen (auf Wunsch auch mit
* Pfad, dabei aber weitaus höherer
* Speicherbedarf!) und gibt bei
* mehrfach belegten Clustern die
* Dateien an (besser: Ist beim
* Scannen der Files ein Cluster schon
* von einer anderen Datei belegt,
* gibt CheckFat deren Namen aus. Wenn
* ein Cluster also viermal belegt
* ist, wird bei drei Dateien der
* Hinweis erscheinen, daß der Cluster
* bereits von der letzten belegt
* ist.) Diese Ausgabe erfolgt nur,
* wenn sie per -x (siehe unten)
* angefordert wurde!
* In diesem Zusammenhang gibt es auch
* drei neue Kommandozeilenoptionen:
* -x meldet, wie gerade beschrieben,
* mehrfach belegte Cluster mit
* dem Dateinamen.
* -l merkt sich die kompletten Pfade
* (normal werden nur die Filenamen
* gespeichert).
* -a gibt bei mehrfach belegten
* Clustern diesen Umstand bei
* jedem beteiligten Cluster aus,
* während dies sonst nur beim
* ersten Mal passiert (die
* restlichen Cluster der Datei
* sind ja damit zwangsweise
* ebenfalls bereits belegt).
* 11.10.1995: Fehler in work_dir behoben, der zum
* Überlauf des scl-Arrays und damit
* zur Überschreibung der Variablen
* drive führte. Außerdem wird jetzt
* zusätzlich geprüft, ob bei der
* Rekursion noch genügend Platz im
* my_path-Array ist.
* Darüberhinaus stimmt jetzt auch der
* Returncode bei Fehlerabbruch, und
* für die zu speichernden Filenamen
* (-x/-l) wird nicht mehr immer ein
* Byte zu wenig angefordert.
* Sicherheitsüberprüfungen für den
* BPB.
* 12.10.1995: Bei Unterverzeichnissen wird jetzt
* geprüft, ob sie als erstes die
* Ordner "." und ".." enthalten. Wenn
* nicht, wird das Verzeichnis nicht
* bearbeitet. Ebenso werden falsche
* Einträge für den Startcluster
* dieser beiden Pseudoverzeichnisse
* gemeldet.
* Kleinen Fehler in der BPB-Prüfung
* entfernt (der ersten Datencluster
* muß hinter dem Wurzelverzeicnis
* beginnen, nicht hinter der 2. FAT).
* 13.10.1995: Freitag, der 13., und trotzdem habe
* ich heute erfahren, daß ich meine
* letzte Vordiplomsklausur bestanden
* habe :)
* Unabhängig davon meldet CheckFat
* jetzt bei der Schlußanalyse der FAT
* auch Cluster, die auf sich selbst
* zeigen.
* 19.10.1995: Compilierung mit Memdebug, um zu
* testen, ob alle mallocs/frees in
* Ordnung sind. Sind sie zum Glück :)
* 20.10.1995: Probleme mit Thing und TOSWIN
* (Parameter werden nicht erkannt),
* daher ein wenig Debug-Output.
* 26.10.1995: Die Probleme lagen an Thing, der
* Debug-Output ist also wieder weg.
* Es gibt eine neue Kommandozeilen-
* Option -d, mit der die Ausgabe von
* als defekt markierten Clustern
* eingeschaltet wird. Bisher wurden
* sie immer ausgegeben und als Fehler
* gemeldet; das hat sich aber als
* unbrauchbar erwiesen, weil Cluster
* 16383 bei größeren Partionen wegen
* eines GEMDOS-Fehlers von den
* meisten Partitionierungsprogrammen
* als defekt markiert wird.
* Die Versionsnummer entfernt, weil
* sie ohnehin recht wenig aussagt.
* 29.10.1995: Neue Option -u, bei der CheckFat
* nicht mehr Dlock benutzt.
* 12.12.1995: Bei einem defekten Cluster 16383
* erfolgt jetzt die Meldung, daß dies
* normalerweise eine Schutzmaßnahme
* gegen einen GEMDOS-Fehler ist.
* 20.12.1995: CheckFat gibt jetzt bei Option -?
* die Kurzanleitung aus (also so, wie
* wie es auch bei Aufruf komplett
* ohne Parameter geschieht).
* 21.12.1995: In der Kurzanleitung fehlte noch
* die gestern eingeführte Option -?.
* 15.01.1996: Letzte Vorbereitungen für die
* Veröffentlichung.
* 18.02.1996: Neue Option -s für eingeschränkte
* "Gesprächigkeit" von CheckFat.
* 27.02.1996: CheckFat meldet jetzt auch bei als
* defekt markiertem Cluster 16384,
* daß dies meistens eine Vorsichts-
* maßnahme ist.
* 01.03.1996: CheckFat macht es jetzt wirklich
* richtig und meldet die Vorsichts-
* maßnahme bei den beiden Clustern,
* die an der 32767-Sektoren-Grenze
* liegen.
* 05.03.1996: Vergleich beider FATs und Meldung
* eventueller Untschiede. Durch die
* neue Option -1 kann man CheckFat
* anweisen, bei Unterschieden zum
* Test FAT 1 zu benutzen.
* Außerdem werden jetzt auch die
* Startcluster vor der Überprüfung
* auf Korrektheit ausgegeben.
* Files mit leerem Cluster sowie
* Verweise auf unbelegte Cluster
* werden jetzt auch bzw. gesondert
* gemeldet.
* Neue Option -f zur Ausgabe des
* Fragmentierungsgrades.
* 06.03.1996: Ausführlichere Ausgabe des
* Fragmentierungsgrades.
* 13.03.1996: Die BPB-Überprüfung testet jetzt
* auch, ob die Sektor- oder Cluster-
* größe 0 ist. Das kann z.B. dann
* passieren, wenn man eine Partition
* mit mehr als 1 GB erstellt hat.
* Sowas sollte ein Partionierer nur
* nach einer Sicherheitsabfrage
* erlauben, weil es unter GEMDOS
* nicht korrekt funktioniert.
* 17.03.1996: Korrekte Überprüfung der FAT-Größe
* im BPB-Test. Außerdem wird jetzt
* eine eigene BPB-Struktur benutzt,
* die mit vorzeichenlosen Integern
* arbeitet (dadurch werden jetzt
* Laufwerke mit 32768 Bytes großen
* Clustern korrekt geprüft).
* Workaround für Partitionen, deren
* Cluster 65536 Bytes groß sind (das
* sind im BPB 0, da nur 16 Bit-Werte
* in der Struktur stehen!) Das GEMDOS
* des Falcon kommt damit zwar klar,
* ratsam ist es jedoch nicht.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <mintbind.h>
#include <portab.h>
#include <memdebug.h>
/* Benötigte Strukturen und Konstanten */
#define VERSION "vom 17.03.1996"
#define NOT16BIT 1
#define NOMEM 2
#define READERROR 3
#define NOBPB 4
#define TOODEEP 5
#define LOCKED 6
#define NOBIOS 7
#define WRONGBPB 8
#define TMFILES 9
#define WRONG 0
#define ILLEGAL 1
#define LOOPS 2
#define DIRLOOPS 3
#define DESTROYED 4
#define EMPTYCLUST 5
#define AMBIGUOUS 6
#define ILLCL 7
#define DAMAGED 8
#define ORPHAN 9
#define MULTIPLE 10
#define ILLSUBDIR 11
#define ILLPSEUDO 12
#define SELFPOINTER 13
#define EMPTYPOINT 14
#define NO_OF_TYPES 15
/*
* Die maximale Rekursionstiefe bestimmt sich in
* etwa durch [*_StkSize / (61 + PATHMAX)] - 4,
* wobei man natürlich eine recht große
* Sicherheitsreserve für den Stack lassen sollte
*/
#define MAXDEPTH 17
/*
* Bestimmt, wie lang ein Pfad inklusive Nullbyte
* maximal sein darf. Sollte diese Grenze während
* der Rekursion innerhalb von work_dir
* überschritten werden, wird CheckFat mit der
* Meldung "Zu tiefe Ordnerverschachtelung"
* abgebrochen.
*/
#define PATHMAX 129
#define MAXCLUST (bpb->numcl + 1)
typedef struct
{
char dir_name[11];
BYTE dir_attr;
BYTE dir_dummy[10];
UWORD dir_time;
UWORD dir_date;
UWORD dir_stcl;
ULONG dir_flen;
} DIR;
typedef struct
{
UWORD recsiz;
UWORD clsiz;
UWORD clsizb;
UWORD rdlen;
UWORD fsiz;
UWORD fatrec;
UWORD datrec;
UWORD numcl;
UWORD bflags;
} _BPB_;
/* Prototypen */
void leave(WORD retcode);
WORD eval_args(WORD argc, char *argv[]);
void usage(void);
void err(WORD code);
void correct_dir(DIR *dir, WORD entries);
void work_dir(DIR *dir, WORD entries, char *path,
UWORD *scl, WORD depth);
void swap(UWORD *value);
void lswap(ULONG *value);
/* Globale Variablen */
char *errtext[] = {
"Kein Fehler!",
"Medium hat keine 16-Bit-Fat!",
"Kein Speicher mehr frei!",
"Lesefehler!",
"Bios-Parameterblock unlesbar!",
"Zu tiefe Ordnerverschachtelung!",
"Laufwerk gesperrt!",
"Kein BIOS-Laufwerk!",
"Bios-Parameterblock fehlerhaft!",
"Zu viele Files!?"},
*reporttext[NO_OF_TYPES] = {
"%d unterschiedliche Dateilänge(n)!\n",
"%d File(s)/Ordner mit illegalem Start/"
"Folgecluster!\n",
"%d File(s)/Ordner mit "
"Clusterschleife!\n",
"%d Ordnerschleife(n)!\n",
"%d File(s)/Ordner mit defektem "
"Cluster!\n",
"%d File(s)/Ordner mit leerem Cluster!\n",
"%d Cluster ohne eindeutigen "
"Vorgänger!\n",
"%d illegale(r) Folgecluster!\n",
"%d als defekt markierte(r) Cluster!\n",
"%d verwaiste(r) Cluster!\n",
"%d mehrfach belegte(r) Cluster!\n",
"%d Verzeichnis(se) ohne '.' und/oder "
"'..'!\n",
"%d falsche(r) Startcluster bei '.' "
"und/oder '..'!\n",
"%d auf sich selbst zeigende(r) "
"Cluster!\n",
"%d Verweis(e) auf leere(n) Cluster!\n"},
*filenames[32768L];
UWORD filenr,
fragments,
fragmented,
min_dist,
max_dist,
*fat,
startclusters[MAXDEPTH];
WORD drive,
to_report[NO_OF_TYPES],
shownames, /* Namen anzeigen */
showclusters, /* Cluster anzeigen */
verbose, /* Ausführliche Meldungen */
crosslinks, /* Namen bei Crosslinks */
showall, /* Alle Crosslinks */
longnames, /* Pfadnamen bei Crosslinks */
showdamaged, /* Defekte Cluster melden */
nolock, /* Device nicht locken */
silent, /* Keine Rahmenausgaben */
usefat1, /* Bei Bedarf FAT1 benutzen */
showfrag, /* Fragmentierung zeigen */
hold, /* Auf Tastendruck warten */
quit,
entries,
*checkfat,
*tempcheck;
LONG lock,
clsiz_b;
ULONG distances;
_BPB_ *bpb;
DIR *rootdir;
void main(WORD argc, char *argv[])
{
UWORD i, j,
length,
free_areas,
free_length,
min_free,
max_free,
critic1,
critic2,
badfat,
*fat2;
WORD differ = 0;
LONG minfat;
fat = 0L;
checkfat = tempcheck = 0L;
rootdir = 0L;
shownames = showclusters = verbose = quit =
hold = showall = longnames = showdamaged =
nolock = usefat1 = showfrag = 0;
/*
* Kommandozeile auswerten, beenden, wenn
* fehlerhaft
*/
if (!eval_args(argc, argv))
leave(2);
/*
* Versuchen, das gewünschte Bios-Device gegen
* GEMDOS-Zugriffe zu schützen. Ist der Aufruf
* vorhanden und es trat dabei ein Fehler auf,
* wird CheckFat mit einer entsprechenden Meldung
* verlassen.
*/
if (!nolock &&
((lock = Dlock(1, drive)) != -32L))
{
if (lock == -46L)
err(NOBIOS);
if (lock != 0L)
err(LOCKED);
}
for (i = 0; i < NO_OF_TYPES; i++)
to_report[i] = 0;
/* Versuchen, den Bios-Parameterblock zu lesen */
if ((bpb = (_BPB_ *)Getbpb(drive)) == 0L)
err(NOBPB);
/*
* Sicherstellen, daß es sich um eine 16-Bit-FAT
* handelt
*/
if ((bpb->bflags & 1) != 1)
err(NOT16BIT);
/*
* Einige Tests, ob es wirklich ein gültiger BPB
* ist:
* - Bytes pro Sektor durch 512 teilbar
* - Bytes pro Sektor nicht Null
* - Bytes pro Cluster = Sektoren pro Cluster *
* Bytes pro Sektor
* - Wurzelverzeichnis mindestens 1 Sektor lang
* - mindestens ein, maximal 32766 Datencluster
* - FAT groß genug für alle Datencluster
* - Erster Datensektor hinter Ende von Directory
*/
if (bpb->recsiz % 512)
err(WRONGBPB);
if (!bpb->recsiz)
err(WRONGBPB);
/* Workaround für Cluster mit 65536 Bytes */
if (((LONG)bpb->recsiz * (LONG)bpb->clsiz) ==
0x10000L)
{
clsiz_b = 0x10000L;
}
else
clsiz_b = bpb->clsizb;
if (clsiz_b != (bpb->clsiz * bpb->recsiz))
err(WRONGBPB);
if (bpb->rdlen < 1)
err(WRONGBPB);
if ((bpb->numcl < 1) || (bpb->numcl > 32766))
err(WRONGBPB);
minfat = ((LONG)(bpb->numcl + 2) * 2L +
((LONG)bpb->recsiz - 1L)) / (LONG)bpb->recsiz;
if ((WORD)minfat > bpb->fsiz)
err(WRONGBPB);
if ((bpb->fatrec + bpb->fsiz + bpb->rdlen) >
bpb->datrec)
{
err(WRONGBPB);
}
/*
* Speicherplatz für Wurzelverzeichnis, FAT und
* zwei Testtabellen anfordern. Gelingt dies
* nicht, Programm mit Meldung verlassen.
*/
if ((rootdir = (DIR *)malloc((LONG)bpb->rdlen
* (LONG)bpb->recsiz)) == 0L)
{
err(NOMEM);
}
if ((fat = (UWORD *)malloc((LONG)bpb->fsiz *
(LONG)bpb->recsiz)) == 0L)
{
err(NOMEM);
}
if ((checkfat = (WORD *)malloc((LONG)bpb->fsiz
* (LONG)bpb->recsiz)) == 0L)
{
err(NOMEM);
}
if ((tempcheck = (WORD *)malloc((LONG)
bpb->fsiz * (LONG)bpb->recsiz)) == 0L)
{
err(NOMEM);
}
fat2 = (UWORD *)tempcheck;
/*
* Anzahl der Einträge im Wurzelverzeichnis
* berechnen
*/
entries = (WORD)((LONG)bpb->rdlen *
bpb->recsiz / 32);
/*
* Die beiden kritischen Cluster berechnen, die
* bei GEMDOS-Versionen kleiner < 0.30 als defekt
* markiert werden sollten und daher von CheckFat
* nur mit Bemerkung gemeldet werden.
*/
critic1 = 32767U / bpb->clsiz;
critic2 = critic1 + 1;
/*
* Wurzelverzeichnis und FATs einlesen. Tritt
* dabei ein Fehler auf, Programm beenden
*/
if (Rwabs(0, (void *)rootdir, bpb->rdlen,
bpb->fatrec + bpb->fsiz, drive))
{
err(READERROR);
}
if (Rwabs(0, (void *)fat, bpb->fsiz,
bpb->fatrec, drive))
{
err(READERROR);
}
if (Rwabs(0, (void *)fat2, bpb->fsiz,
bpb->fatrec - bpb->fsiz, drive))
{
err(READERROR);
}
if (!silent)
{
printf("CheckFat: Prüfe FAT von Laufwerk "
"%c...\n", (char)(drive + 65));
}
/*
* FAT-Einträge in Motorola-Format wandeln und
* vergleichen
*/
for (i = 0; i <= MAXCLUST; i++)
{
swap(&fat[i]);
swap(&fat2[i]);
if (fat[i] != fat2[i])
{
if (verbose)
{
printf("Eintrag %u in beiden FATs nicht "
"identisch!\n", i);
}
else if (!differ)
{
printf("FATs sind ab Eintrag %u nicht "
"identisch!\n", i);
}
differ = 1;
}
}
/*
* Soll bei Ungleichheit FAT 1 benutzt werden,
* einfach die betroffenen Pointer tauschen
*/
if (differ && usefat1)
{
tempcheck = (WORD *)fat;
fat = fat2;
}
memset(filenames, 0, sizeof(char *) * 32768L);
memset(checkfat, 0, (LONG)bpb->fsiz *
(LONG)bpb->recsiz);
memset(tempcheck, 0, (LONG)bpb->fsiz *
(LONG)bpb->recsiz);
filenr = fragments = fragmented = max_dist = 0;
min_dist = 0xffffU;
distances = 0;
/*
* Wurzelverzeichnis bearbeiten. Da die Funktion
* work_dir rekursiv arbeitet, wird dadurch der
* gesamte Dateibaum durchgetestet. Die globale
* Variable quit wird auf einen Wert != 0 gesetzt,
* wenn dabei ein Lesefehler oder Speichermangel
* aufgetreten ist. In diesem Fall wird CheckFat
* mit einer Meldung beendet.
*/
work_dir(rootdir, entries, "\\",
startclusters, 0);
if (quit)
err(quit);
if (!silent)
puts("\nCheckFat-Ergebnis:");
/* Fragmentierungsgrad ausgeben, wenn gewünscht */
if (showfrag)
{
if (filenr && fragmented)
{
printf("%u Prozent der Files/Ordner (%u "
"von %u) sind fragmentiert!\n",
(UWORD)(+((ULONG)fragmented * 100UL) /
(ULONG)filenr), fragmented, filenr);
if (verbose)
{
printf("Clustersprünge insgesamt: %u\n",
fragments);
printf("Mittlere Anzahl von Cluster"
"sprüngen pro fragmentierter/m Datei/"
"Ordner: %u\n", fragments / fragmented);
printf("Mittlere Länge eines Sprungs: "
"%lu Cluster (minimal: %u, maximal: "
"%u)\n", distances / (ULONG)fragments,
min_dist, max_dist);
}
}
else
{
puts("Es gibt keine fragmentierten Dateien/"
"Ordner!");
}
free_areas = free_length = max_free = 0;
min_free = 0xffffU;
for (i = 2; i <= MAXCLUST;) /* Kein Smiley */
{
if (fat[i])
{
for (j = i + 1; (j <= MAXCLUST) && fat[j];
j++);
i = j;
}
else
{
free_areas++;
length = 0;
for (j = i; (j <= MAXCLUST) && !fat[j];
j++)
{
length++;
}
if (length < min_free)
min_free = length;
if (length > max_free)
max_free = length;
free_length += length;
i = j;
}
}
if (free_areas)
{
if (free_areas > 1)
{
printf("Der freie Platz ist in %u "
"Bereiche zerteilt!\n", free_areas);
if (verbose)
{
printf("Durchschnittliche Länge eines "
"Bereichs: %u Cluster (%lu Bytes)\n",
free_length / free_areas,
(ULONG)(free_length / free_areas) *
(ULONG)clsiz_b);
printf("Kleinster Bereich: %u Cluster "
"(%lu Bytes)\n", min_free,
(ULONG)min_free * (ULONG)clsiz_b);
printf("Größter Bereich: %u Cluster "
"(%lu Bytes)\n", max_free,
(ULONG)max_free * (ULONG)clsiz_b);
}
}
else
{
printf("%lu Bytes in %u Cluster(n) sind "
"an einem Stück frei!\n",
(ULONG)free_length * (ULONG)clsiz_b,
free_length);
}
}
}
/*
* Alle (gültigen) Clustereinträge durchgehen und
* Fehler melden bzw. merken (ersteres nur, wenn
* ausführliche Ausgabe per -v gewünscht ist)
*/
memset(tempcheck, 0, (LONG)bpb->fsiz *
(LONG)bpb->recsiz);
for (i = 2; i <= MAXCLUST; i++)
{
/*
* Prüfen, ob weitere Cluster auf den Folgecluster
* des aktuellen zeigen oder ob der Folgecluster
* nicht belegt ist
*/
if ((fat[i] >= 2) && (fat[i] <= MAXCLUST))
{
if (!fat[fat[i]])
{
if (verbose)
{
printf("Cluster %u verweist auf einen "
"unbelegten Cluster!\n", i);
}
to_report[EMPTYPOINT]++;
}
if (tempcheck[fat[i]] > 0)
{
if (verbose)
{
printf("Folgende Cluster zeigen alle "
"auf Cluster %u:\n", fat[i]);
printf("%u %u ", tempcheck[fat[i]], i);
for (j = i + 1; j <= MAXCLUST; j++)
{
if (fat[j] == fat[i])
printf("%u ", j);
}
puts("");
}
to_report[AMBIGUOUS]++;
tempcheck[fat[i]] = -1;
}
else
{
if (!tempcheck[fat[i]])
tempcheck[fat[i]] = i;
}
}
/* Prüfen, ob Cluster mehrfach belegt ist */
if (checkfat[i] > 1)
{
if (verbose)
{
printf("Cluster %u ist %d-fach belegt!\n",
i, checkfat[i]);
}
to_report[MULTIPLE]++;
}
/* Prüfen, ob Cluster verwaist ist */
if ((fat[i] > 1) && ((fat[i] <= MAXCLUST) ||
(fat[i] == 0xffffU)) && (checkfat[i] == 0))
{
if (verbose)
printf("Cluster %u ist verwaist!\n", i);
to_report[ORPHAN]++;
}
/* Prüfen, ob Cluster gültigen Folgecluster hat */
if (fat[i] && ((fat[i] < 2) ||
((fat[i] < 0xfff0U) &&
(fat[i] > MAXCLUST))))
{
if (verbose)
{
printf("Cluster %u hat illegalen "
"Folgecluster!\n", i);
}
to_report[ILLCL]++;
}
/*
* Prüfen, ob Cluster als defekt markiert ist. Die
* Ausgabe erfolgt nur, wenn Option -d aktiv ist.
*/
if (showdamaged && (fat[i] >= 0xfff0U) &&
(fat[i] <= 0xfff7U))
{
if (verbose)
{
printf("Cluster %u ist als defekt "
"markiert!\n", i);
if ((i == critic1) || (i == critic2))
{
puts("Dies ist in der Regel nur ein "
"Schutz gegen einen GEMDOS-Fehler!");
}
}
to_report[DAMAGED]++;
}
/* Prüfen, ob Cluster auf sich selbst verweist */
if (fat[i] == i)
{
if (verbose)
{
printf("Cluster %u zeigt auf sich "
"selbst!\n", i);
}
to_report[SELFPOINTER]++;
}
}
/* Eventuell gefundene Fehler berichten */
badfat = differ;
if (differ)
puts("Unterschiedliche FATs!");
for (i = 0; i < NO_OF_TYPES; i++)
{
badfat += to_report[i];
if (to_report[i])
printf(reporttext[i], to_report[i]);
}
if (!badfat && !silent)
puts("Alles OK!");
/*
* Am Schluß den Speicher und das Laufwerk wieder
* freigeben und 0 zurückliefern, wenn kein Fehler
* gefunden wurde, sonst 3
*/
for (i = 32767U; i > 0; i--)
{
if (filenames[i])
free(filenames[i]);
}
if (filenames[i])
free(filenames[i]);
free((void *)tempcheck);
free((void *)checkfat);
free((void *)fat);
free((void *)rootdir);
if (!nolock && (lock == 0L))
Dlock(0, drive);
if (badfat)
leave(3);
else
leave(0);
}
/*
* leave
*
* Verläßt CheckFat mit einem gegebenen Returncode
* und wartet dabei gegebenenfalls vorher auf eine
* Taste.
*
* Eingabe:
* retcode: Zurückzuliefernder Returncode
*/
void leave(WORD retcode)
{
if (hold)
{
if (!silent)
puts("\nBitte eine Taste drücken!");
Cnecin();
}
exit(retcode);
}
/*
* eval_args
*
* Wertet die Kommandozeile aus, die CheckFat
* übergeben wurde.
*
* Eingabe:
* argc: Anzahl der Elemente in argv
* argv: Stringarray mit einzelnen Optionen
* (argc und argv haben exakt die gleiche
* Bedeutung wie bei main!)
*
* Rückgabe:
* 0: Parameter waren fehlerhaft
* 1: Parameter OK
*/
WORD eval_args(WORD argc, char *argv[])
{
WORD i, j;
drive = -1;
if (argc < 2)
{
usage();
return(0);
}
for (i = 1; i < argc; i++)
{
if (*argv[i] == '-')
{
if (strlen(argv[i]) == 1)
{
puts("CheckFat: Falsches Optionsformat "
"- ignoriert");
continue;
}
for (j = 1; j < strlen(argv[i]); j++)
{
switch (argv[i][j])
{
case 'v':
verbose = 1;
break;
case 'n':
shownames = 1;
break;
case 'c':
shownames = showclusters = 1;
break;
case 'a':
showall = 1;
break;
case 'x':
crosslinks = 1;
break;
case 'l':
crosslinks = longnames = 1;
break;
case 'd':
showdamaged = 1;
break;
case 'u':
nolock = 1;
break;
case 's':
silent = 1;
break;
case '1':
usefat1 = 1;
break;
case 'f':
showfrag = 1;
break;
case 'h':
hold = 1;
break;
case '?':
usage();
return(0);
default:
printf("CheckFat: Unbekannte Option "
"'%c' - ignoriert\n", argv[i][j]);
}
}
}
else
{
drive = (*argv[i] & ~32) - 65;
if ((drive < 0) || (drive > 31))
{
puts("CheckFat: Falsche "
"Laufwerksangabe!");
return(0);
}
if (i != (argc - 1))
{
puts("CheckFat: Überflüssige Parameter "
"- ignoriert");
}
break;
}
}
if (drive == -1)
{
puts("CheckFat: Laufwerksangabe fehlt!");
return(0);
}
return(1);
}
/*
* usage
*
* Gibt die Kurzanleitung aus.
*/
void usage(void)
{
puts("CheckFat "VERSION);
#ifdef STCOMPUTER
puts("(c) 1996 by MAXON Computer GmbH");
#endif
puts("Geschrieben in Pure C von Thomas Binder");
puts("\nAufruf: checkfat [Optionen] Laufwerk");
puts("\nOptionen:");
puts("\t-v: Ausführliche FAT-Fehlermeldungen");
puts("\t-n: Alle Filenamen anzeigen");
puts("\t-c: Alle Cluster zu allen Files "
"anzeigen (schließt -n ein)");
puts("\t-a: Alle mehrfach belegten Cluster "
"einer Datei anzeigen");
puts("\t-x: Bei mehrfach belegten Clustern "
"auch die anderen Dateinamen melden");
puts("\t-l: Komplette Pfadnamen bei -x melden "
"(schließt -x ein, hoher Speicher-\n"
"\t bedarf!)");
puts("\t-d: Bei der Ergebnisausgabe als "
"defekt markierte Cluster melden");
puts("\t-1: Bei Ungleichheit FAT 1 als "
"Prüfgrundlage benutzen");
puts("\t-f: Fragmentierungsinformationen "
"ausgeben");
puts("\t-u: Laufwerk nicht mit Dlock sperren");
puts("\t-s: Keine unnötigen Ausgaben");
puts("\t-h: Nach Programmende auf Tastendruck "
"warten");
puts("\t-?: Dieser Hilfetext");
}
/*
* work_dir
*
* Bearbeitet alle Files eines Verzeichnisses und
* alle Unterverzeichnisse. Gefunden werden dabei
* Clusterschleifen, Ordnerschleifen, illegale
* Start- und Folgecluster sowie Files mit
* defekten Clustern. Außerdem werden alle
* belegten Cluster vermerkt, um später mehrfach
* vergebene und verwaiste Cluster erkennen zu
* können.
*
* Eingabe:
* dir: Zeiger auf das zu bearbeitende Verzeichnis
* entries: Maximale Anzahl an Einträgen in dir
* path: Pfadname von dir (mit abschließendem \\)
* scl: Array mit Anfangsclustern der
* "Elternverzeichnisse" von dir
* depth: Bisherige Schachtelungstiefe, 0 =
* Wurzelverzeichnis. scl enthält somit
* depth Einträge.
*/
void work_dir(DIR *dir, WORD entries, char *path,
UWORD *scl, WORD depth)
{
WORD i, j, k,
frag,
dist,
first;
UWORD cl,
lastcl;
ULONG clusts,
must;
DIR *subdir;
char my_path[PATHMAX],
fname[13];
/*
* Sollte maximale Schachtelungstiefe erreicht
* sein, Funktion abbrechen und Fehler melden
*/
if (depth == MAXDEPTH)
{
if (!shownames)
printf("%s: ", path);
puts("Pfad zu tief verschachtelt!\n");
quit = TOODEEP;
return;
}
/* Einträge des Verzeichnisses umwandeln */
correct_dir(dir, entries);
/*
* Prüfen, ob die beiden ersten Einträge . und ..
* heissen und Verzeichnisse sind. Wenn nicht,
* Verzeichnis nicht bearbeiten. Dieser Test muß
* natürlich im Wurzelverzeichnis entfallen.
*/
if (depth)
{
if (strncmp(dir[0].dir_name, ". ",
11) || strncmp(dir[1].dir_name,
".. ", 11) ||
!(dir[0].dir_attr & 16) ||
!(dir[1].dir_attr & 16))
{
if (!shownames)
printf("%s: ", path);
puts("Kein '.' und/oder '..'!");
to_report[ILLSUBDIR]++;
return;
}
/*
* Jetzt noch testen, ob . und .. die richtigen
* Startcluster haben. Falls nicht, wird dies
* gemeldet, das Verzeichnis aber trotzdem
* bearbeitet.
*/
if (dir[0].dir_stcl != scl[depth - 1])
{
if (!shownames)
printf("%s: ", path);
puts("Falscher Startcluster für '.'!");
to_report[ILLPSEUDO]++;
}
if (dir[1].dir_stcl != ((depth == 1) ? 0 :
scl[depth - 2]))
{
if (!shownames)
printf("%s: ", path);
puts("Falscher Startcluster für '..'!");
to_report[ILLPSEUDO]++;
}
}
/* Alle Einträge durchgehen */
for (i = (depth) ? 2 : 0; i < entries; i++)
{
/*
* Ist das erste Zeichen des aktuellen Filenamens
* eine Null, ist der letzte Eintrag erreicht
*/
if (!dir[i].dir_name[0])
break;
/*
* Eintrag nur überprüfen, wenn es kein gelöschtes
* File und kein Laufwerkslabel ist
*/
if ((dir[i].dir_name[0] == (char)0xe5) ||
(dir[i].dir_attr & 8))
{
continue;
}
frag = 0;
/*
* Startcluster ermitteln und aktuelle Filenummer
* erhöhen. Durch diese Nummer erhält jedes File
* eine individuelle Kennzeichnung, mit der es
* möglich ist, Clusterschleifen zuverlässig zu
* erkennen. Da filenr ein 16-Bit-Wert ist, darf
* ein Laufwerk maximal 65535 Dateien/Ordner
* beherbergen; allerdings ist diese Grenze schon
* technisch nicht erreichbar, da eine 16-Bit-Fat
* maximal 32766 Dateicluster haben kann. Außerdem
* ermöglicht es diese individuelle Nummer, bei
* bereits belegten Clustern zu ermitteln, zu
* welcher Datei sie gehören.
*/
cl = dir[i].dir_stcl;
if ((filenr++) == 32768U)
{
quit = TMFILES;
return;
}
/* Den aktuellen Filenamen basteln */
for (j = 0; j < 8; j++)
{
if (dir[i].dir_name[j] != ' ')
fname[j] = dir[i].dir_name[j];
else
break;
}
fname[j] = 0;
if (dir[i].dir_name[8] != ' ')
{
fname[j++] = '.';
for (k = 0; k < 3; k++)
{
if (dir[i].dir_name[8 + k] != ' ')
fname[j + k] = dir[i].dir_name[8 + k];
else
break;
}
fname[j + k] = 0;
}
if (dir[i].dir_attr & 16)
{
/*
* Eintrag ist Ordner, also neuen Pfadnamen
* zusammensetzen; dabei vorher prüfen, ob my_path
* noch genügend Platz bietet; wenn nicht, wird
* TOODEEP gemeldet
*/
if ((strlen(path) + 2 + strlen(fname)) >
PATHMAX)
{
printf("%s%s\\ ist zu lang!\n", path,
fname);
quit = TOODEEP;
return;
}
strcpy(my_path, path);
strcat(my_path, fname);
strcat(my_path, "\\");
if (shownames)
{
printf("%s ", my_path);
if (showclusters)
printf("%u ", cl);
}
/*
* Startcluster überprüfen und notfalls
* beanstanden. Bei Ordnern wird auch Null als
* Startcluster beanstandet, da Ordner nie
* komplett leer sein dürfen (sie müssen
* mindestens . und .. enthalten).
*/
if ((cl < 2) || (cl > MAXCLUST))
{
if (!shownames)
printf("%s: ", my_path);
puts("Illegaler Startcluster!");
to_report[ILLEGAL]++;
continue;
}
/*
* Prüfen, ob dieser Startcluster im aktuellen
* Pfad schon einmal vorhanden war. Wenn ja, zeigt
* das neue Verzeichnis auf einen seiner
* Vorgänger, es gibt also eine Ordnerschleife.
*/
for (j = 0; j < depth; j++)
{
if (scl[j] == cl)
{
if (!shownames)
printf("%s: ", my_path);
puts("Ordnerschleife!");
to_report[DIRLOOPS]++;
break;
}
}
if (j != depth)
continue;
/*
* Bei aktiver Kommandozeilenoption -x den Ordner-
* bzw. Pfadnamen speichern. Ist dazu nicht mehr
* genug Speicher vorhanden, wird work_dir
* abgebrochen. Die Speicherung erfolgt erst
* jetzt, weil vorher noch nicht sichergestellt
* war, ob der Ordner überhaupt gültig ist, was
* unter Umständen Speicher spart.
*/
if (crosslinks)
{
if (longnames)
{
filenames[filenr] =
malloc(strlen(my_path) + 1);
}
else
{
filenames[filenr] =
malloc(strlen(fname) + 1);
}
if (filenames[filenr] == 0L)
{
if (shownames)
puts("");
quit = NOMEM;
return;
}
if (longnames)
strcpy(filenames[filenr], my_path);
else
{
strcpy(filenames[filenr], fname);
strcat(filenames[filenr], "\\");
}
}
/*
* Den Startcluster in der Liste vermerken und
* Speicher für ein neues Verzeichnis anfordern
*/
scl[depth] = cl;
if ((subdir = (DIR *)malloc(clsiz_b)) != 0L)
{
/*
* War Speicher vorhanden, alle Cluster des neuen
* Directories ermitteln und prüfen
*/
clusts = first = 0;
while (cl < 0xfff0U)
{
/* Testen, ob aktueller Cluster gültig ist */
if ((cl < 2) || ((cl > MAXCLUST) &&
(cl < 0xfff0)))
{
if (!shownames)
printf("%s: ", my_path);
puts("Illegaler Folgecluster!");
to_report[ILLEGAL]++;
break;
}
/*
* Testen, ob der Cluster überhaupt belegt ist;
* falls nicht, trotzdem einlesen
*/
if (!fat[cl])
{
if (!shownames)
printf("%s: ", my_path);
puts("Enthält leeren Cluster!");
to_report[EMPTYCLUST]++;
}
/*
* Gehört der aktuelle Cluster bereits zu diesem
* Ordner, liegt eine Clusterschleife vor
*/
if (tempcheck[cl] == filenr)
{
if (!shownames)
printf("%s: ", my_path);
puts("Clusterschleife!");
to_report[LOOPS]++;
break;
}
/*
* War der Cluster bereits belegt, dies (ggf. mit
* File- bzw. Pfadnamen der Datei, die ihn belegt)
* ausgeben (je nach Einstellung von showall
* nur, wenn es der erste Cluster war). Dabei wird
* die Einleseschleife nicht abgebrochen, da das
* Unterverzeichnis ja noch weitergeht (wenn auch
* nicht mehr ganz korrekt).
*/
if (tempcheck[cl])
{
if (showall || !first)
{
first = 1;
if (!shownames)
printf("%s: ", my_path);
if (crosslinks)
{
printf("Cluster %d bereits durch "
"%s belegt!\n", cl,
filenames[tempcheck[cl]]);
}
else
{
printf("Cluster %d bereits "
"belegt!\n", cl);
}
}
}
/*
* War alles OK, den aktuellen Cluster einlesen.
* Tritt dabei ein Fehler auf, die Routine
* abbrechen.
*/
if (Rwabs(0, (void *)((LONG)subdir +
clusts * clsiz_b),
bpb->clsiz, bpb->datrec + (cl - 2) *
bpb->clsiz, drive))
{
free((void *)subdir);
if (shownames)
puts("");
quit = READERROR;
break;
}
/*
* In der FAT-Testtabelle den Belegungsgrad des
* aktuellen Clusters erhöhen und vermerken, daß
* er bereits zu diesem Ordner gehört
*/
checkfat[cl]++;
tempcheck[cl] = filenr;
/*
* Folgecluster ermitteln und ggf. neuen Speicher
* anfordern
*/
lastcl = cl;
cl = fat[cl];
clusts++;
if (!cl)
break;
if (cl < 0xfff0U)
{
if (showclusters)
printf("%u ", cl);
/* Clustersprung merken, falls nötig */
if (showfrag && ((lastcl + 1) != cl))
{
if (!frag)
fragmented++;
fragments++;
if (verbose)
{
dist = abs(lastcl - cl);
distances += dist;
if (dist < min_dist)
min_dist = dist;
if (dist > max_dist)
max_dist = dist;
}
frag = 1;
}
subdir = (DIR *)realloc((void *)
subdir, (clusts + 1L) * clsiz_b);
if (subdir == 0L)
{
quit = NOMEM;
if (shownames)
puts("");
}
}
}
if (!quit)
{
/*
* Alle Cluster sind ermittelt. War der letzte
* Cluster ein als defekt markierter, dieses
* vermerken.
*/
if ((cl >= 0xfff0) && (cl < 0xfff8U))
{
if (!shownames)
printf("%s: ", my_path);
puts("Enthält defekten Cluster!");
to_report[DESTROYED]++;
}
if (shownames)
puts("");
/*
* work_dir rekursiv mit dem neu eingelesenen
* Verzeichnis aufrufen
*/
work_dir(subdir, (WORD)(clusts *
clsiz_b / 32L), my_path, scl,
depth + 1);
free(subdir);
}
}
else
{
quit = NOMEM;
if (shownames)
puts("");
}
}
else
{
/*
* Aktueller Verzeichniseintrag ist eine Datei.
* Auch hier zunächst den Startcluster prüfen.
*/
if (shownames)
{
printf("%s%s ", path, fname);
if (showclusters)
printf("%u ", cl);
}
clusts = first = 0;
/*
* Bei Dateien Startcluster Null nicht
* beanstanden, es handelt sich hierbei um
* reguläre Files mit Länge Null
*/
if (!cl)
{
if (shownames)
puts("");
continue;
}
/* Startcluster prüfen */
if ((cl < 2) || (cl > MAXCLUST))
{
if (!shownames)
printf("%s%s: ", path, fname);
puts("Illegaler Startcluster!");
to_report[ILLEGAL]++;
}
else
{
/*
* War Startcluster OK, aus der Dateilänge
* berechnen, wieviele Cluster diese Datei
* belegen muß. Danach alle Cluster des Files
* durchgehen. Zuvor wird noch, falls nötig (-x),
* der Datei- bzw. Pfadname gespeichert.
*/
if (crosslinks)
{
if (longnames)
{
filenames[filenr] = malloc(
strlen(path) + strlen(fname) + 1);
}
else
{
filenames[filenr] =
malloc(strlen(fname) + 1);
}
if (filenames[filenr] == 0L)
{
quit = NOMEM;
if (shownames)
puts("");
return;
}
if (longnames)
{
strcpy(filenames[filenr], path);
strcat(filenames[filenr], fname);
}
else
strcpy(filenames[filenr], fname);
}
must = (dir[i].dir_flen / clsiz_b);
if (dir[i].dir_flen % clsiz_b)
must++;
while (cl < 0xfff0U)
{
/* Cluster auf Gültigkeit prüfen */
if ((cl < 2) || ((cl > MAXCLUST) &&
(cl < 0xfff0)))
{
if (!shownames)
printf("%s%s: ", path, fname);
puts("Illegaler Folgecluster!");
to_report[ILLEGAL]++;
break;
}
/*
* Prüfen, ob der Cluster überhaupt belegt ist;
* falls nicht, trotzdem mitzählen
*/
if (!fat[cl])
{
if (!shownames)
printf("%s%s: ", path, fname);
puts("Enthält leeren Cluster!");
to_report[EMPTYCLUST]++;
}
/*
* War der aktuelle Cluster schon durch diese
* Datei belegt, Clusterschleife melden
*/
if (tempcheck[cl] == filenr)
{
if (!shownames)
printf("%s%s: ", path, fname);
puts("Clusterschleife!");
to_report[LOOPS]++;
break;
}
/*
* Ist der Cluster bereits durch eine andere
* Datei belegt, dieses melden. Die Schleife wird
* dabei nicht abgebrochen, da die Datei dadurch
* ja noch nicht beendet ist.
*/
if (tempcheck[cl])
{
if (showall || !first)
{
first = 1;
if (!shownames)
printf("%s%s: ", path, fname);
if (crosslinks)
{
printf("Cluster %d bereits durch "
"%s belegt!\n", cl,
filenames[tempcheck[cl]]);
}
else
{
printf("Cluster %d bereits "
"belegt!\n", cl);
}
}
}
/*
* Jetzt Belegungsgrad des aktuellen Clusters
* erhöhen und vermerken, daß er bereits in diesem
* File benutzt wurde
*/
checkfat[cl]++;
tempcheck[cl] = filenr;
/*
* Folgecluster ermitteln und Anzahl der durch das
* File tatsächlich belegten Cluster um eins
* erhöhen
*/
lastcl = cl;
cl = fat[cl];
clusts++;
if (!cl)
break;
if (cl < 0xfff0U)
{
if (showclusters)
printf("%u ", cl);
/* Clustersprung merken, falls nötig */
if (showfrag && ((lastcl + 1) != cl))
{
if (!frag)
fragmented++;
fragments++;
if (verbose)
{
dist = abs(lastcl - cl);
distances += dist;
if (dist < min_dist)
min_dist = dist;
if (dist > max_dist)
max_dist = dist;
}
frag = 1;
}
}
}
/*
* Sind alle Cluster geprüft, testen, ob das Ende
* der Verkettung durch einen defekten Cluster
* markiert wurde. Wenn ja, dies vermerken.
*/
if ((cl >= 0xfff0) && (cl < 0xfff8U))
{
if (!shownames)
printf("%s%s: ", path, fname);
puts("Enthält defekten Cluster!");
to_report[DESTROYED]++;
}
else
{
/*
* Ansonsten prüfen, ob die Datei die richtige
* Anzahl von Clustern belegt
*/
if (clusts != must)
{
if (!shownames)
printf("%s%s: ", path, fname);
puts("Abweichende Längen!");
to_report[WRONG]++;
continue;
}
}
if (shownames)
puts("");
}
}
if (quit)
return;
}
}
/*
* correct_dir
*
* Wandelt die Einträge eines eingelesenen
* Verzeichnisses vom Intel- in's Motorola-Format
* um.
*
* Eingabe:
* dir: Zeiger auf Verzeichnis
* entries: Anzahl zu wandelnder Einträge in dir
*/
void correct_dir(DIR *dir, WORD entries)
{
WORD i;
for (i = 0; i < entries; i++)
{
swap(&dir[i].dir_time);
swap(&dir[i].dir_date);
swap(&dir[i].dir_stcl);
lswap(&dir[i].dir_flen);
}
}
/*
* swap
*
* 16-Bit-Wert vom Intel- in's Motorola-Format
* umwandeln (und umgekehrt).
*
* Eingabe:
* value: Zeiger auf zu wandelndes Wort
*/
void swap(UWORD *value)
{
*value = ((*value & 255) << 8) + (*value >> 8);
}
/*
* lswap
*
* Wie swap, jedoch für 32-Bit-Werte.
*
* Eingabe:
* value: Zeiger auf zu wandelnden Long
*/
void lswap(ULONG *value)
{
UWORD high, low;
low = (UWORD)(*value & 65535L);
high = (UWORD)(*value >> 16);
swap(&low);
swap(&high);
*value = ((LONG)low << 16) + (LONG)high;
}
/*
* err
*
* Fehlerbehandlungsroutine. Wird angesprungen,
* wenn während des Programmablaufs ein Fehler
* aufgetreten ist. Hier wird, wenn nötig, aller
* angeforderter Speicher freigegeben und eine
* Meldung ausgegeben, welcher Fehler aufgetreten
* ist. Die Funktion kehrt nicht zurück, sondern
* beendet das Programm.
*
* Eingabe:
* code: Fehlercode
*/
void err(WORD code){
WORD i;
/* Wenn nötig, Speicher freigeben */
for (i = 32767; i >= 0; i--)
{
if (filenames[i])
free(filenames[i]);
}
if (tempcheck != 0L)
free((void *)tempcheck);
if (checkfat != 0L)
free((void *)checkfat);
if (fat != 0L)
free((void *)fat);
if (rootdir != 0L)
free((void *)rootdir);
/* Meldung ausgeben */
printf("CheckFat: %s\n", errtext[code]);
/* Laufwerk freigeben, wenn nötig */
if (!nolock && (lock == 0L))
Dlock(0, drive);
/* Und tschüß... */
leave(1);
}
/* EOF */